# camera_window.py
import sys
import cv2
import datetime
import json
import os
import numpy as np
import config
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableWidgetItem, QAbstractItemView, QHeaderView, QSizePolicy
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtWidgets import QItemDelegate, QComboBox
from api import fetch_courses, fetch_students, add_attendance

from camera_window_ui import Ui_MainWindow

class ComboBoxDelegate(QItemDelegate):
    def __init__(self, parent=None, items=None, students=None, camera_window=None):
        super().__init__(parent)
        self.items = items
        self.students = students
        self.camera_window = camera_window

    def createEditor(self, parent, option, index):
        editor = QComboBox(parent)
        editor.addItems(self.items)
        
        # 连接信号到槽函数
        editor.currentIndexChanged.connect(lambda: self.on_currentIndexChanged(editor, index))
        return editor

    def setEditorData(self, editor, index):
        value = index.model().data(index, Qt.EditRole)
        editor.setCurrentText(value)

    def setModelData(self, editor, model, index):
        model.setData(index, editor.currentText(), Qt.EditRole)

    def updateEditorGeometry(self, editor, option, index):
        editor.setGeometry(option.rect)

    def on_currentIndexChanged(self, editor, index):
        new_status = editor.currentText()
        row = index.row()
        if self.students and row < len(self.students):
            self.students[row]['status'] = new_status
            if self.camera_window:
                self.camera_window.update_student_status(row, new_status)



class CameraWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        # 初始化摄像头
        self.cap = None
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_frame)

        self.known_faces, self.labels = self.load_known_faces()  # 在初始化时加载已知人脸数据
        self.recognizer = cv2.face.LBPHFaceRecognizer_create()
        self.train_recognizer()  # 训练人脸识别器

        # 加载Haar级联分类器
        self.face_cascade = cv2.CascadeClassifier(config.HAAR_CASCADE_PATH)  # 使用配置文件中的路径
        if self.face_cascade.empty():
            self.statusBar.showMessage("无法加载Haar级联分类器", 5000)
            return
        
        # 获取课程数据并填充下拉框
        api_url = config.BASE_URL + 'course/list' 
        data = fetch_courses(api_url)
        if isinstance(data, list):  # 确保data是一个列表
            self.course_data = data  # 保存课程数据以便后续使用
            processed_data = [f"{course['name']} - {course['teacher']}" for course in data]
            self.course_combo.addItems(processed_data)

            # 设置默认选择
            if self.course_combo.count() > 0:
                self.course_combo.setCurrentIndex(0)
                self.on_course_selected(0)  # 手动调用一次以测试
        else:
            self.statusBar.showMessage("获取课程数据失败", 5000)
            return

        # 连接信号和槽
        self.course_combo.currentIndexChanged.connect(self.on_course_selected)
        # 确保按钮绑定只执行一次
        if not hasattr(self, 'button_bound'):
            try:
                self.button.clicked.disconnect()
            except TypeError:
                pass
            self.button.clicked.connect(self.on_button_clicked)
            self.button_bound = True

        # 设置表格自动填充满
        self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

        # 设置表格高度策略
        size_policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.tableWidget.setSizePolicy(size_policy)

        # 启动摄像头
        self.start_camera()

        self.attendance_delegate = ComboBoxDelegate(self, ["缺勤", "出勤", "请假"], self.students, self)
        self.tableWidget.setItemDelegateForColumn(5, self.attendance_delegate)

    def __del__(self):
        self.stop_camera()

    def start_camera(self):
        if not self.cap:
            self.cap = cv2.VideoCapture(0)
            if not self.cap.isOpened():
                self.statusBar.showMessage("无法打开摄像头", 5000)
                return
            
            # 设置摄像头分辨率为 1920x1080 或其他高分辨率
            self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
            self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)

            self.timer.start(30)  # 每30毫秒更新一次帧
            self.statusBar.showMessage("摄像头已打开")

    def stop_camera(self):
        if self.cap:
            self.timer.stop()
            self.cap.release()
            self.cap = None
            self.statusBar.showMessage("摄像头已关闭")

    def update_frame(self):
        ret, frame = self.cap.read()
        if ret:
            # 转换为灰度图像
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

            # 进行人脸检测
            faces = self.face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

            # 在检测到的人脸上绘制矩形框
            for (x, y, w, h) in faces:
                cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 5)

                # 截取人脸区域
                face_roi = gray[y:y+h, x:x+w]

                # 进行人脸匹配
                self.match_face(face_roi)

            # 转换颜色格式
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, ch = frame.shape
            bytes_per_line = ch * w
            qt_image = QImage(frame.data, w, h, bytes_per_line, QImage.Format_RGB888)
            pixmap = QPixmap.fromImage(qt_image)
            self.label.setPixmap(pixmap.scaled(self.label.width(), self.label.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def on_course_selected(self, index):
        selected_course = self.course_combo.itemText(index)
        self.statusBar.showMessage(f"已选择课程: {selected_course}")
        if 0 <= index < len(self.course_data):
            self.course_info = self.course_data[index]
            if isinstance(self.course_info, dict):
                course_details = f"课程编号：{self.course_info.get('id', '')}\n课程名称: {self.course_info.get('name', '')}\n任课教师: {self.course_info.get('teacher', '')}\n选修类型: {self.course_info.get('type', '')}\n学分: {self.course_info.get('credit', '')}"
                # 安全地解析rules字段
                rules_str = self.course_info.get('rules', '{}')
                try:
                    rules_str = json.loads(rules_str)
                    if isinstance(rules_str, dict):
                        today = datetime.datetime.now().strftime('%Y-%m-%d')
                        if today in rules_str:
                            course_details += f"\n\n上课时间:{rules_str[today]}"
                            self.todayTime = rules_str[today]
                        else:
                            course_details += f"\n\n上课时间: 今天没有安排课程"
                    else:
                        course_details += "\n\n上课时间: 规则格式不正确"
                except json.JSONDecodeError:
                    course_details += "\n\n上课时间: 无法解析规则"
            else:
                course_details = "课程信息格式不正确"
        else:
            course_details = "无效的课程索引"
        self.course_info_label.setText(course_details)

        # 调用新的API接口获取课程详细信息
        api_url = config.BASE_URL + "assigned/list"
        course_id = str(self.course_info.get('id', ''))
        self.students = fetch_students(api_url, course_id)
        if self.students:
            self.fill_table(self.students)
        else:
            self.statusBar.showMessage("获取课程详细信息失败", 5000)


    def fill_table(self, students):
        # 清空表格
        self.tableWidget.clearContents()
        self.tableWidget.setRowCount(0)

        # 设置表格可编辑
        self.tableWidget.setEditTriggers(QAbstractItemView.AllEditTriggers)

        # 只显示必要的字段
        headers = ['学号', '学生姓名', '课程名称', '专业', '上课时间', '考勤状态']
        self.tableWidget.setColumnCount(len(headers))
        self.tableWidget.setHorizontalHeaderLabels(headers)

        row_count = 0
        for student in students:
            if isinstance(student, dict):
                self.tableWidget.insertRow(row_count)
                self.tableWidget.setItem(row_count, 0, QTableWidgetItem(str(student.get('studentNum', ''))))
                self.tableWidget.setItem(row_count, 1, QTableWidgetItem(str(student.get('studentName', ''))))
                self.tableWidget.setItem(row_count, 2, QTableWidgetItem(str(student.get('courseName', ''))))
                self.tableWidget.setItem(row_count, 3, QTableWidgetItem(str(student.get('studentMajor', ''))))
                self.tableWidget.setItem(row_count, 4, QTableWidgetItem(str(self.todayTime)))
                self.tableWidget.setItem(row_count, 5, QTableWidgetItem(str('缺勤')))
                student['status'] = '缺勤'
                self.students[row_count] = student
                row_count += 1
        else:
            self.statusBar.showMessage("课程详细信息格式不正确", 5000)

    
    def update_student_status(self, row, status):
        if self.students and row < len(self.students):
            self.students[row]['status'] = status
            self.statusBar.showMessage(f"学生 {self.students[row]['studentName']} 的考勤状态已更新为 {status}")
    def on_button_clicked(self):
        if not self.students:
            self.statusBar.showMessage("没有学生数据", 5000)
            return
        api_url = config.BASE_URL + 'attendance/add'
        success_count = 0
        failure_messages = []

        for student in self.students:
            cTime = datetime.datetime.now().strftime('%Y-%m-%d') + " | " + self.todayTime
            data = {
                    'courseId': student['courseId'],
                    'studentId': student['studentId'],
                    'teacherId': self.course_info['teacherId'],
                    'courseTime': str(cTime),
                    'status': student['status']
                }
            add_attendance(api_url, data)
        message = f"共提交 {len(self.students)} 条考勤记录，成功 {success_count} 条"
        if failure_messages:
            message += f"，失败 {len(failure_messages)} 条:\n" + "\n".join(failure_messages)
        self.statusBar.showMessage(message, 5000)

    
    def load_known_faces(self):
        known_faces = {}
        labels = {}
        faces_dir = config.FACE_PATH
        label_counter = 0

        for filename in os.listdir(faces_dir):
            if filename.endswith(".jpg"):
                face_path = os.path.join(faces_dir, filename)
                face_image = cv2.imread(face_path, cv2.IMREAD_GRAYSCALE)
                if face_image is not None:
                    known_faces[filename] = face_image
                    labels[label_counter] = filename
                    label_counter += 1
                else:
                    print(f"无法读取文件: {face_path}")

        return known_faces, labels
    
    def train_recognizer(self):
        faces = []
        face_labels = []

        for label, filename in self.labels.items():
            faces.append(self.known_faces[filename])
            face_labels.append(label)

        if faces:
            self.recognizer.train(faces, np.array(face_labels))
            print("人脸识别器训练完成")
        else:
            print("没有已知人脸数据")
    
    def match_face(self, face_roi):
        known_faces, labels = self.load_known_faces()

        # 初始化人脸识别器
        recognizer = cv2.face.LBPHFaceRecognizer_create()
        faces = []
        face_labels = []

        for label, filename in labels.items():
            faces.append(known_faces[filename])
            face_labels.append(label)

        if faces:
            recognizer.train(faces, np.array(face_labels))
            # 进行人脸识别
            label, confidence = recognizer.predict(face_roi)
            if confidence < 50:
                matched_filename = labels.get(label)
                if matched_filename:
                    for index, student in enumerate(self.students):
                        if student['face'] == matched_filename:
                            student['status'] = '出勤'
                            self.students[index] = student
                            self.tableWidget.item(index, 5).setText('出勤')
                            self.statusBar.showMessage(f"匹配成功，学生 {student['studentName']} ,置信度: {confidence}")
                            break
                    print(self.students)
                    return
            else:
                self.statusBar.showMessage(f"匹配失败，置信度: {confidence}")
        else:
            self.statusBar.showMessage("没有已知人脸数据")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = CameraWindow()
    window.show()
    sys.exit(app.exec_())
